package com.caishi.lkx.exam.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.caishi.lkx.exam.mapper.ChapterMapper;
import com.caishi.lkx.exam.mapper.ExChapterPaperGenerateMapper;
import com.caishi.lkx.exam.mapper.ScatteredQuestionMapper;
import com.caishi.lkx.exam.model.ChapterModel;
import com.caishi.lkx.exam.service.IChapterService;
import com.caishi.lkx.exam.service.IExColumnConfigReService;
import com.caishi.lkx.exam.service.IQuestionService;
import com.caishi.lkx.exam.vo.QuestionCollectVo;
import com.caishi.lkx.exam.vo.SpecialConditionVo;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.IBaseMapper;
import com.zzw.common.entity.impl.BaseEntity;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.threadpool.SysThreadPool;
import com.caishi.lkx.exam.ienum.type.ColumnType;
import com.caishi.lkx.exam.ienum.type.QuestionCollectionType;

import com.caishi.lkx.exam.model.re.ExChapterPaperGenerateModel;
import com.caishi.lkx.exam.model.re.ExColumnConfigReModel;
import com.caishi.lkx.exam.records.RecordsCollectType;
import com.caishi.lkx.exam.records.RecordsGetDataService;
import com.caishi.lkx.exam.records.dto.CollectionErrorAndCollectRecordDataDto;
import com.caishi.lkx.exam.records.dto.QuestionRecordsDto;
import com.caishi.lkx.exam.util.QuestionUtil;
import lombok.SneakyThrows;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Future;
import java.util.stream.Stream;

import static com.zzw.common.cache.CacheConstants.BIG_UP_UP;

@Service
public class ChapterServiceImpl extends AbstractQuestionCollectionService<ChapterModel, String> implements IChapterService {

    @Resource
    private ChapterMapper chapterMapper;

    @Resource
    private ScatteredQuestionMapper scatteredQuestionMapper;

    @Resource
    private RecordsGetDataService recordsGetDataService;

    @Resource
    private IExColumnConfigReService exColumnConfigReService;

    @Resource
    private IQuestionService questionService;
    @Resource
    private ExChapterPaperGenerateMapper chapterPaperGenerateMapper;

    @Override
    public IBaseMapper<ChapterModel, String> getMapper() {
        return chapterMapper;
    }

    @Override
    @Cacheable(value = BIG_UP_UP, key = "'tree:chapter:' + #parent.id + #n", unless = "#result == null")
    public ChapterModel setChildren(ChapterModel parent, int n) {
        return IChapterService.super.setChildren(parent, n);
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public ChapterModel insert(ChapterModel entity) {
        updateQcount(entity, entity.getQcount());
        entity.setQcount(QuestionUtil.getCollectionQuestionCount(entity.getContent()));
        entity.setLeaf(true);
        if (ObjectUtil.isNotEmpty(entity.getParentId()) && !entity.getParentId().equals("0")) {
            ChapterModel parent = getById(entity.getParentId());
            if (parent.getLeaf()) {
                parent.setLeaf(false);
                getMapper().updateById(parent);
            }
        }
        entity = getMapper().insert(entity) == 1 ? entity : null;
        updatePath(entity);
        return  entity;
    }

    private void updatePath(ChapterModel entity) {
        if (StrUtil.isNotBlank(entity.getPath())) return;
        var parent = getById(entity.getParentId());
        String subPath;
        if (parent == null) {
            subPath = "";
        } else {
            String path = parent.getPath();
            if(StrUtil.isBlank(path)){
                subPath = parent.getId();
            }else{
                subPath = path + "/" + parent.getId();
            }

        }
        entity.setPath(subPath);
        getMapper().updateById(entity);
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public ChapterModel update(ChapterModel entity) {
        entity.setQcount(QuestionUtil.getCollectionQuestionCount(entity.getContent()));
        var old = getById(entity.getId());
        if (!old.getQcount().equals(entity.getQcount())) {
            updateQcount(entity, entity.getQcount() - old.getQcount());
        }
        return IChapterService.super.update(entity);
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    public ChapterModel simpleUpdate(ChapterModel entity) {
        return owner(IChapterService.class).update(entity);
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    public ChapterModel baseUpdate(ChapterModel entity) {
        return this.getMapper().updateById(entity) > 0 ? entity : null;
    }


    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    public Boolean delete(String aLong) {
        ChapterModel entity = this.getById(aLong);
        updateQcount(entity, -entity.getQcount());
        IChapterService.super.delete(aLong);
        return this.delChildrenChapter(aLong);
    }

    @Override
    @CacheEvict(value = BIG_UP_UP, allEntries = true)
    public Boolean delete(List<String> longs) {
        for (String id : longs) {
            ChapterModel entity = this.getById(id);
            updateQcount(entity, -entity.getQcount());
        }
        IChapterService.super.delete(longs);
        return this.delChildrenChapter(longs);
    }

    @Override
    public int delete(LambdaUpdateWrapper<ChapterModel> wrapper) {
        throw new BizRuntimeException();
    }

    @Override
    public int delete(UpdateWrapper<ChapterModel> wrapper) {
        throw new BizRuntimeException();
    }

    @SneakyThrows
    @Override
    public QuestionCollectVo clientCollectionData(String userId, Long groupId, String id, String industryId, String examId, Long categoryId,boolean setRightAnswer) {
        Future<QuestionRecordsDto> future = null;
        Future<CollectionErrorAndCollectRecordDataDto> collectFuture = null;
        if (StrUtil.isNotEmpty(userId)) {
            future = SysThreadPool.submit(() -> recordsGetDataService.getQuestionRecordsResult(userId, industryId, examId, categoryId, RecordsCollectType.chapter, id));
            collectFuture = SysThreadPool.submit(() -> recordsGetDataService.getCollectInCollectDataResult(userId, industryId, examId, categoryId, RecordsCollectType.chapter, id));
        }
        var result = super.clientCollectionData(userId, groupId, id, industryId, examId, categoryId,true);
        if (future != null) {
            coreQuestionCollectionService.safeProcess(future.get(), collectFuture.get(), result, userId, id, setRightAnswer);
        } else if (groupId != null) {
            coreQuestionCollectionService.safeProcess(new QuestionRecordsDto(), new CollectionErrorAndCollectRecordDataDto(), result, groupId.toString(), id, false);
        }
        result.setRecordsType(RecordsCollectType.chapter);
        return result;
    }

    @Override
    QuestionCollectionType nowCollectionType() {
        return QuestionCollectionType.chapter;
    }


    @Override
    public List<ChapterModel> categoryTopChapter(Long categoryId) {
        var cids = exColumnConfigReService.columnTypeConfigs(categoryId, ColumnType.chapterPractice);
        if (cids.isEmpty()) return Collections.emptyList();
        return selectBatchIds(cids);
    }

    @Override
    public List<ChapterModel> categoryAllChapter(Long categoryId) {
        var cids = exColumnConfigReService.columnTypeConfigs(categoryId, ColumnType.chapterPractice);
        return selectBatchIds(cids).stream().flatMap(ch -> {
            if (ch.getLeaf()) return Stream.of(ch);
            var all = new LinkedList<ChapterModel>();
            all.add(ch);
            all.addAll(allChildren(ch.getId()));
            return all.stream();
        }).toList();
    }

    @Override
    public List<ChapterModel> categoryAllLeafChapter(Long categoryId) {
        var cids = exColumnConfigReService.columnTypeConfigs(categoryId, ColumnType.chapterPractice);
        return selectBatchIds(cids).stream().flatMap(ch -> {
            if (ch.getLeaf()) return Stream.of(ch);
            return allChildren(ch, -1, wrappers().eq(ChapterModel::getLeaf, true)).stream();
        }).toList();
    }


    @Transactional(rollbackFor = Exception.class)
    public void importData(List<ChapterModel> insert, List<ExColumnConfigReModel> re) {
        if (!insert.isEmpty()) chapterMapper.insertBatch(insert);
        if (!re.isEmpty()) exColumnConfigReService.insertBatch(re);
    }

    public int activation(ChapterModel chapterModel) {
        return this.chapterMapper.updateDelete(chapterModel);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ChapterModel categoryGenerateChapter(Long categoryId,
                                                Long questionSourceCategoryId,
                                                SpecialConditionVo condition,
                                                String chapterName,
                                                String columnId,
                                                List<String> inIds, String parentId) {
        if (null != inIds && inIds.isEmpty()) {
            return null;
        }
        var content = questionService.generateQuestionBlock(questionSourceCategoryId, condition.getTypes(), condition.getMaxCount(), inIds, null, null);
        List<String> ids = QuestionUtil.getCollectionQuestionAllIds(content);
        if (null != inIds && !inIds.isEmpty()) {
            inIds.removeAll(ids);
        }
        var model = new ChapterModel();
        model.setName(chapterName);
        model.setCategoryId(categoryId);
        model.setContent(content);
        model.setQcount(QuestionUtil.getCollectionQuestionCount(content));
        if (StrUtil.isNotBlank(parentId)) {
            model.setParentId(parentId);
        }
        insert(model);
        if (StrUtil.isBlank(parentId)) {
            exColumnConfigReService.insert(ExColumnConfigReModel.builder()
                    .columnId(columnId)
                    .configId(model.getId())
                    .build());
        }
        return model;
    }

    @Override
    public List<ChapterModel> chapterByQuestionId(Long cid, String questionId) {
        return this.chapterMapper.selectList(Wrappers.<ChapterModel>lambdaQuery()
                .eq(ChapterModel::getCategoryId, cid)
                .eq(ChapterModel::getLeaf, true)
                .apply("json_contains( `content`, json_object('qs', json_array(json_object('id', '" + questionId + "'))))")
        );
    }

    @Override
    public Boolean delChildrenChapter(String id) {
        return this.delChildrenChapter(ListUtil.list(false, id));
    }

    @Override
    public Boolean delChildrenChapter(List<String> ids) {
        for (String id : ids) {
            if (StrUtil.isNotBlank(id)) {
                List<ChapterModel> chapterModels = this.selectList(Wrappers.<ChapterModel>lambdaUpdate().like(ChapterModel::getPath, id));
                List<String> otherIds = chapterModels.stream().map(ChapterModel::getId).toList();
                List<String> list = new ArrayList<>(otherIds);
                list.add(id);
                if (!otherIds.isEmpty()) {
                    IChapterService.super.delete(otherIds);
                }
                for (String otherId : list) {
                    this.deleteChapterPaperSet(otherId);
                }

            }
        }
        return true;
    }

    @Override
    public List<String> getQuestionIdsByChapter(List<String> ids) {
        List<ChapterModel> chapterModels = this.selectList(Wrappers.<ChapterModel>query().select("content->'$**.id' as question_ids").in("id", ids));
        return chapterModels.stream().filter(Objects::nonNull).flatMap(f -> f.getQuestionIds().stream()).filter(Objects::nonNull).toList();
    }

    @Override
    public void saveChapterPaperSet(Long categoryId, List<String> chapterIds, List<String> paperIds) {
        ExChapterPaperGenerateModel model = new ExChapterPaperGenerateModel();
        model.setCategoryId(categoryId);
        model.setChapterIds(chapterIds);
        model.setPaperIds(paperIds);
        chapterPaperGenerateMapper.insert(model);
    }

    @Override
    public void deleteChapterPaperSet(String chapterId) {
        if (StrUtil.isBlank(chapterId)) return;
        ExChapterPaperGenerateModel exChapterPaperGenerateModel = chapterPaperGenerateMapper.selectFirst(Wrappers.<ExChapterPaperGenerateModel>lambdaQuery().apply("JSON_CONTAINS(chapter_ids,JSON_ARRAY('" + chapterId + "'))"));
        if (null == exChapterPaperGenerateModel) return;
        List<String> chapterIds = exChapterPaperGenerateModel.getChapterIds();
        Long count = this.selectCount(Wrappers.<ChapterModel>lambdaQuery().in(ChapterModel::getId, chapterIds));
        if (count == 0) {
            chapterPaperGenerateMapper.deleteById(exChapterPaperGenerateModel);
        }
    }

    private void updateQcount(ChapterModel model, Integer count) {
        if (count == null || count == 0) return;
        var parent = getById(model.getParentId());
        if (parent == null) {
            return;
        }
        chapterMapper.update(null, Wrappers.<ChapterModel>lambdaUpdate()
                .setSql(String.format("qcount = qcount + %d", count))
                .eq(BaseEntity::getId, parent.getId())
        );
        updateQcount(parent, count);
    }

}
